package edu.kufpg.armatus.activity;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.Trie;
import org.apache.commons.collections4.trie.PatriciaTrie;
import android.os.Bundle;
import android.text.Selection;
import android.text.Spannable;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import com.google.common.base.Strings;
import com.google.common.collect.Range;
import com.google.common.collect.RangeMap;
import com.google.common.collect.TreeRangeMap;
import edu.kufpg.armatus.R;
import edu.kufpg.armatus.activity.SelectionTextView.SelectionWatcher;
import edu.kufpg.armatus.data.Crumb;
import edu.kufpg.armatus.data.Glyph;
import edu.kufpg.armatus.gesture.OnPinchZoomListener;
import edu.kufpg.armatus.util.TurboImageButton;
public class ConsoleEntrySelectionActivity2 extends ConsoleEntryActivity {
private static final String DELIMETER = "×"; // Uncommon character
private SelectionTextView mTextView;
private RangeMap<Integer, Glyph> mRangeGlyphMap = TreeRangeMap.create();
private int mSelStart = -1;
private int mSelEnd = -1;
private ScaleGestureDetector mScaleGestureDetector;
private Trie<String, Range<Integer>> mGlyphPathTrie = new PatriciaTrie<Range<Integer>>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.console_entry_selection_activity2);
mTextView = (SelectionTextView) findViewById(R.id.console_entry_selection_view2);
mTextView.addSelectionWatcher(new SelectionWatcher() {
@Override
public void onSelectionChanged(int selStart, int selEnd) {
mSelStart = selStart;
mSelEnd = selEnd;
}
});
mTextView.setCustomSelectionActionModeCallback(new GlyphSelectionCallback());
mTextView.setText(getEntry().getCommandResponse().getGlyphText());
final OnPinchZoomListener zoomListener = new OnPinchZoomListener(this, (int) mTextView.getTextSize()) {
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
final int tempSelStart = mSelStart;
final int tempSelEnd = mSelEnd;
mTextView.setTextSize(getIntSize());
mTextView.post(new Runnable() {
@Override
public void run() {
Selection.setSelection((Spannable) mTextView.getText(), tempSelStart, tempSelEnd);
mTextView.performLongClick();
}
});
super.onScaleEnd(detector);
}
};
mScaleGestureDetector = new ScaleGestureDetector(this, zoomListener);
// RangeMap/Trie initialization
int index = 0;
for (Glyph glyph : getEntry().getCommandResponse().getGlyphs()) {
if (!glyph.getText().isEmpty()) {
Range<Integer> glyphRange = Range.closedOpen(index, index + glyph.getText().length());
mRangeGlyphMap.put(glyphRange, glyph);
String pathStr = pathToString(glyph.getPath());
mGlyphPathTrie.put(pathStr, glyphRange);
index += glyph.getText().length();
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleGestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
private static String pathToString(List<Crumb> path) {
StringBuilder pathStrBuilder = new StringBuilder("");
if (!path.isEmpty()) {
pathStrBuilder.append(path.get(0));
int size = path.size();
for (int i = 1; i < size; i++) {
pathStrBuilder.append(DELIMETER);
pathStrBuilder.append(path.get(i).toString());
}
}
return pathStrBuilder.toString();
}
private class GlyphSelectionCallback implements Callback {
private TurboImageButton mPrevGlyph, mNextGlyph, mParentGlyph;
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.console_entry_selection_action_mode, menu);
menu.removeItem(android.R.id.selectAll);
menu.removeItem(android.R.id.copy);
mode.setTitle("Text selection");
View actionView = menu.findItem(R.id.console_entry_selection_glyph_navigation).getActionView();
mPrevGlyph = (TurboImageButton) actionView.findViewById(R.id.console_entry_selection_prev_glyph);
mPrevGlyph.enableTurbo();
ImageButton wrapGlyphs = (ImageButton) actionView.findViewById(R.id.console_entry_selection_wrap_glyphs);
mNextGlyph = (TurboImageButton) actionView.findViewById(R.id.console_entry_selection_next_glyph);
mNextGlyph.enableTurbo();
mParentGlyph = (TurboImageButton) actionView.findViewById(R.id.console_entry_selection_parent_glyph);
mParentGlyph.enableTurbo();
mPrevGlyph.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Map.Entry<Range<Integer>, Glyph> entry = mRangeGlyphMap.getEntry(mSelStart - 1);
if (entry != null) {
mSelStart = entry.getKey().lowerEndpoint();
Selection.setSelection((Spannable) mTextView.getText(), mSelStart, mSelEnd);
}
}
});
wrapGlyphs.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mSelStart = mRangeGlyphMap.getEntry(mSelStart).getKey().lowerEndpoint();
mSelEnd = mRangeGlyphMap.getEntry(mSelEnd - 1).getKey().upperEndpoint();
Selection.setSelection((Spannable) mTextView.getText(), mSelStart, mSelEnd);
}
});
mNextGlyph.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Map.Entry<Range<Integer>, Glyph> entry = mRangeGlyphMap.getEntry(mSelEnd);
if (entry != null) {
mSelEnd = entry.getKey().upperEndpoint();
Selection.setSelection((Spannable) mTextView.getText(), mSelStart, mSelEnd);
}
}
});
mParentGlyph.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
RangeMap<Integer, Glyph> subMap = mRangeGlyphMap.subRangeMap(Range.closedOpen(mSelStart, mSelEnd));
String prefix = "";
for (Glyph glyph : subMap.asMapOfRanges().values()) {
if (!glyph.getPath().isEmpty()) {
if (prefix.isEmpty()) {
prefix = pathToString(glyph.getPath());
} else {
prefix = Strings.commonPrefix(prefix, pathToString(glyph.getPath()));
}
}
}
int index = prefix.lastIndexOf(DELIMETER);
if (index == -1) {
mSelStart = 0;
mSelEnd = mTextView.length();
} else {
String parentPrefix = prefix.substring(0, index);
Range<Integer> parentRange = Range.singleton(mSelStart);
for (Range<Integer> range : mGlyphPathTrie.prefixMap(parentPrefix).values()) {
parentRange = parentRange.span(range);
}
mSelStart = parentRange.lowerEndpoint();
mSelEnd = parentRange.upperEndpoint();
}
Selection.setSelection((Spannable) mTextView.getText(), mSelStart, mSelEnd);
}
});
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mPrevGlyph.disableTurbo();
mNextGlyph.disableTurbo();
}
}
}